feat: add opt-in real nickname controls#8713
Conversation
新增用户真实昵称展示相关配置开关,支持在用户识别元数据中追加真实昵称,或在开启后仅向模型提供真实昵称。 将用户和群组身份信息改为结构化 JSON 元数据,并对昵称、群名等外部输入进行清洗,降低控制字符、零宽字符和伪造标签边界对提示词结构的干扰。 补充 WebUI 配置文案与条件项过渡动画,并增加单元测试覆盖默认行为、真实昵称回退、清洗、去重和对象形态 raw_message 读取。
There was a problem hiding this comment.
Code Review
This pull request introduces features to append and display a user's real nickname in system reminders, along with robust metadata sanitization and formatting. Key changes include adding helper functions to clean metadata values (removing control characters, zero-width characters, and escaping HTML-like tags), updating the configuration schema and localized strings, and wrapping configuration items in the dashboard with a TransitionGroup for smoother UI transitions. Unit tests were also added to verify the new metadata processing and fallback behaviors. Feedback on the changes highlights a potential layout issue in the dashboard transition styles: hardcoding max-height to 180px in the TransitionGroup styles can cause items taller than 180px to be clipped or experience sudden height jumps during transitions. It is recommended to remove the max-height transition and only animate opacity and transform to ensure compatibility with varying item heights.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- In
_append_system_reminders, real-nickname de-duplication currently comparesreal_nickname(unsanitized) againstsanitized_user_nickname; if you want to ensure zero-width/control-character differences don’t cause redundant metadata, consider sanitizingreal_nicknamebefore the equality check and reuse that sanitized value in_format_metadata. - In
AstrBotConfigV4.vue,createSelectorModel(itemKey).valueis called multiple times per item (including insidev-ifs andv-for), which can lead to unnecessary recomputation; you could cache the model per item (e.g., via a computed or local variable inside the loop) to avoid repeated calls.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `_append_system_reminders`, real-nickname de-duplication currently compares `real_nickname` (unsanitized) against `sanitized_user_nickname`; if you want to ensure zero-width/control-character differences don’t cause redundant metadata, consider sanitizing `real_nickname` before the equality check and reuse that sanitized value in `_format_metadata`.
- In `AstrBotConfigV4.vue`, `createSelectorModel(itemKey).value` is called multiple times per item (including inside `v-if`s and `v-for`), which can lead to unnecessary recomputation; you could cache the model per item (e.g., via a computed or local variable inside the loop) to avoid repeated calls.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
移除条件配置项过渡动画中的 max-height 动画,避免较高配置项在进入或离开过渡时被裁切或出现高度跳变。
|
关于 Sourcery 的两点建议,已逐项确认:
补充:关于 Gemini 对 已重新执行 |
撤销此前移除 max-height 过渡的调整。该调整会让条件配置项关闭时直接消失,缺少折叠收起动画;恢复原有高度过渡以保持展开和收起体验一致。
有关这个轻量过渡动画,是否与这个PR关联不大? |
嗯,要说和功能呢其实关联不大,只是我设计的时候希望让新功能在日常不开启用户识别的情况下会被折叠,只有开启后才会展开,这样更加符合直觉也不需要更多的hint来介绍,过渡动画是为了可以让展开更加优美,不然只剩下硬切,体验上会差一点,不过我可以试试把动画提交成另外一个 PR ? |
|
ok,我先改一下,把这段过渡动画先去掉,折叠逻辑代码量不多,我觉得没必要去掉,然后到时候有关过渡动画的我再提一个pr,顺便到时候看看能不能做的更好一点) |
移除当前真实昵称控制 PR 中的通用配置项过渡动画改动,仅保留与真实昵称开关直接相关的条件显示逻辑。这样可以降低当前 PR 的审核范围,后续如需优化配置项出现和消失体验,可单独提交 UI 优化 PR。
|
ok,改完了,已经把这段过渡动画从当前 PR 里去掉了,只保留条件显示/折叠逻辑。 过渡动画相关的 本地重新检查过:
|
恢复普通配置项容器上原本存在的 config-item 类名,避免移除过渡动画时引入无关样式差异。\n\n同时移除样式块末尾多余空行,让前端 diff 保持精简。
|
多删了一行,已修复 |
| user_id = event.message_obj.sender.user_id | ||
| user_nickname = event.message_obj.sender.nickname | ||
| system_parts.append(f"User ID: {user_id}, Nickname: {user_nickname}") | ||
| real_nickname = ( |
There was a problem hiding this comment.
这一块赋值夹杂条件判断,后面判断又依赖这里的赋值。是否可以适当重构来增加可读性?
There was a problem hiding this comment.
现在这段写法确实有一点嵌套,我先去把两个开关先拆成布尔变量试试
There was a problem hiding this comment.
新提交 b722f12 把两个开关拆成更明确的布尔变量,然后拆开了最终昵称和是否追加真实昵称的中间变量,行为保持不变
将真实昵称展示和仅真实昵称两个配置拆成明确的布尔变量。\n\n拆出最终昵称和是否追加真实昵称的中间变量,降低条件判断和赋值交织带来的阅读成本,保持现有行为不变。
| real_nickname = ( | ||
| _sanitize_optional_metadata_value(_get_real_sender_nickname(event)) | ||
| if real_nickname_display_enabled | ||
| else None | ||
| ) | ||
| resolved_nickname = display_nickname | ||
| if real_nickname_only_enabled and real_nickname is not None: | ||
| resolved_nickname = real_nickname | ||
| sanitized_user_nickname = _sanitize_metadata_value(resolved_nickname) | ||
| user_metadata = {"user_id": user_id, "nickname": sanitized_user_nickname} | ||
| should_append_real_nickname = ( | ||
| real_nickname is not None | ||
| and not real_nickname_only_enabled | ||
| and real_nickname != sanitized_user_nickname | ||
| ) | ||
| if should_append_real_nickname: | ||
| user_metadata["real_nickname"] = real_nickname |
There was a problem hiding this comment.
我想这样会精简些?
| real_nickname = ( | |
| _sanitize_optional_metadata_value(_get_real_sender_nickname(event)) | |
| if real_nickname_display_enabled | |
| else None | |
| ) | |
| resolved_nickname = display_nickname | |
| if real_nickname_only_enabled and real_nickname is not None: | |
| resolved_nickname = real_nickname | |
| sanitized_user_nickname = _sanitize_metadata_value(resolved_nickname) | |
| user_metadata = {"user_id": user_id, "nickname": sanitized_user_nickname} | |
| should_append_real_nickname = ( | |
| real_nickname is not None | |
| and not real_nickname_only_enabled | |
| and real_nickname != sanitized_user_nickname | |
| ) | |
| if should_append_real_nickname: | |
| user_metadata["real_nickname"] = real_nickname | |
| resolved_nickname = _sanitize_metadata_value(display_nickname) | |
| append_real_nickname = None | |
| if real_nickname_display_enabled: | |
| if real_nickname := _sanitize_optional_metadata_value(_get_real_sender_nickname(event)): | |
| if real_nickname_only_enabled: | |
| resolved_nickname = real_nickname | |
| elif real_nickname != resolved_nickname: | |
| append_real_nickname = real_nickname | |
| user_metadata = {"user_id": user_id, "nickname": resolved_nickname} | |
| if append_real_nickname: | |
| user_metadata["real_nickname"] = append_real_nickname | |
There was a problem hiding this comment.
我改进一下,先把昵称清洗了,然后再用一个单独变量表示是否需要追加真实昵称,确实会比现在更加直观一点,不过这里我不打算直接用海象运算符,主要是这段本身就是为了提升可读性,普通if写法对后续维护者会更容易读一些,我按这个思路再改一版
| def _sanitize_optional_metadata_value(value: object) -> str | None: | ||
| sanitized = _sanitize_metadata_value(value) | ||
| return sanitized or None |
There was a problem hiding this comment.
>>> _sanitize_optional_metadata_value("\t").__repr__()
'None'
>>> _sanitize_optional_metadata_value(" ").__repr__()
'None'
>>> _sanitize_optional_metadata_value("None").__repr__()
"'None'"
输入空白字符组成的字符串会输出None.
也许有风险?我不确定会不会有什么平台允许空白字符当昵称。
或许把两个方法反一下,_sanitize_metadata_value过滤None为字符串会好些?
There was a problem hiding this comment.
输入空白字符组成的字符串会输出None.
也许有风险?我不确定会不会有什么平台允许空白字符当昵称。
我觉得不应该改。这是符合我们需求的
真实昵称是可选补充信息,如果清洗后只剩空白,就应该视为“不可用”,然后回退到群昵称。我们甚至已经有测试覆盖这个行为:test_append_system_reminders_real_only_falls_back_after_sanitizing。如果这里不返回 None,反而可能导致“仅真实昵称”模式把正常群昵称替换成空昵称,这才是风险
There was a problem hiding this comment.
好的。
我注意到你的代码路径中,原始用户群昵称也经过了sanitized_user_nickname.
如果原始昵称为空白字符的话,最终到达user_metadata.nickname的值会是一个空字符串,没有任何字符。这里会有问题吗?
There was a problem hiding this comment.
这个问题确实存在,如果有平台可以将原始昵称和群昵称都改成空白,当前的代码状态的确也是传一个空白的昵称进去,不过这是原本就存在的一个小问题,而且当前的逻辑已经比较完备了。假设有平台能做到这样,那么原版的代码也是传一个空白昵称进去,我已经做了挺多加固了,比如说这个平台的某个用户,群昵称是正常的但是真实昵称改成了空白,当前的代码逻辑在不开启“仅使用真实昵称”的情况下也是把群昵称和真实昵称一起传进去,如果使用者开了只传真实昵称的这个功能,代码检测到某一个用户的昵称清洗完是空白,也会fallback到群昵称传进去,针对群昵称和原始昵称都是空白的状态,我目前还没有想到十分完美的解决方案,以后可能可以开个新的PR来单独讨论和优化一下
先清洗展示昵称,再通过 append_real_nickname 单独表示是否追加真实昵称。\n\n避免使用海象运算符,保持可读性,同时延续真实昵称为空时回退到展示昵称的行为。
在群聊场景中,当前用户识别信息里的
nickname通常来自群昵称或群名片。这个值更适合作为群内展示名,但它可能被频繁修改,也可能和用户真实昵称不一致。对依赖用户识别、长期上下文或个性化回复的场景来说,模型只能看到群昵称会影响身份信息的一致性。本次改动增加两个默认关闭的真实昵称相关开关,让部署者可以按需选择“追加真实昵称”或“仅使用真实昵称”。未启用新开关时,现有行为保持不变。
同时,本次改动将用户和群组身份信息整理为结构化 JSON 元数据,并对昵称、真实昵称、群名等外部输入做基础清洗,减少控制字符、零宽字符和伪造标签边界对提示词结构的干扰。
Modifications / 改动点
provider_settings.real_nickname_display开关,开启后在支持的平台上向模型额外提供用户真实昵称。provider_settings.real_nickname_only开关,开启后模型只看到真实昵称;当真实昵称不可用或清洗后为空时,回退到原昵称。User metadata: {"user_id":"...","nickname":"..."}。raw_message读取。Screenshots or Test Results / 运行截图或测试结果
已执行以下验证:
本地手动验证:
http://localhost:3000/#/config#normal。Checklist / 检查清单
Summary by Sourcery
Add configurable support for exposing users’ real nicknames and structured metadata in system reminders, along with UI and test updates.
New Features:
Enhancements:
Tests: